home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / lib / evolution / 2.28 / csv2vcard next >
Text File  |  2009-11-03  |  7KB  |  237 lines

  1. #!/usr/bin/perl -w
  2. #
  3. # cvs2vcard - Script to convert Outlook CSV files into VCard files
  4. # suitable to be imported into Evolution.
  5. #
  6. # Copyright (C) 2001 Ximian, Inc.
  7. #
  8. # This program is free software; you can redistribute it and/or
  9. # modify it under the terms of version 2 of the GNU General Public
  10. # License as published by the Free Software Foundation.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15. # General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public
  18. # License along with this program; if not, write to the
  19. # Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  20. # Boston, MA 02110-1301, USA.
  21. #
  22. # Author: Michael MacDonald <mjmac@ximian.com>
  23. #
  24.  
  25. use strict;
  26. use diagnostics;
  27. use Text::ParseWords;
  28.  
  29. sub usage
  30. {
  31.   print STDERR << "--EndOfUsage";
  32.  
  33. Takes a CSV-formatted list of contacts from Outlook and attempts to
  34. convert it into a list of VCards suitable for import into Evolution.
  35.  
  36. Usage: $0 [infile outfile]
  37.  
  38. --EndOfUsage
  39.  
  40.   exit;
  41. }
  42.  
  43. sub is_recognized_format
  44. {
  45.   my $line = shift;
  46.  
  47.   # Making some assumptions here...  Prolly OK.
  48.   return $line =~ /(First Name|Middle Name|Last Name)/; 
  49. }
  50.  
  51. sub map_columns
  52. {
  53.   my $line = shift;
  54.  
  55.   my @names = parse_line(',', 0, $line);
  56.  
  57.   my $ctr = 0;
  58.   my %fieldmap = map { $_ => $ctr++ } @names;
  59.  
  60.   return %fieldmap;
  61. }
  62.  
  63. sub build_vcard_attr_from_def
  64. {
  65.   my ($def, $fields, $map) = @_;
  66.  
  67.   # Valid chars for lookup (from Outlook CSV) are
  68.   # A-Za-z0-9_-'/
  69.   # Valid chars for formatting of attr are
  70.   # \s,|
  71.   my @lookup = map { s/=0A$//; s/[^\w\s\-'\/]//; $_; } split /[\s,]*\|[\s,]*/, $def;
  72.  
  73.   foreach my $el (@lookup) {
  74.     unless (defined($map->{ $el })) {
  75.       print STDERR "$el is undefined\n";
  76.       next;
  77.     }
  78.     if (defined($fields->[$map->{ $el }])) {
  79.       unless ($fields->[$map->{ $el }] =~ /(^$|0\/0\/00)/) {
  80.         $def =~ s/$el/$fields->[$map->{ $el }]/;
  81.       } else {
  82.         $def =~ s/((?<=\|)\s*)?$el(\s*?(?=\|))?(=0A)?,?//;
  83.       }
  84.     } else {
  85.       $def =~ s/((?<=\|)\s*)?$el(\s*?(?=\|))?(=0A)?,?//;
  86.     }
  87.   }
  88.   # Get rid of field delimiters
  89.   $def =~ s/\|//g;
  90.   # Snip off any trailing semicolons or whitespace
  91.   $def =~ s/[\s;]*$//;
  92.  
  93.   return $def;
  94. }
  95.  
  96. sub build_vcard_from_line {
  97.   my ($line, %map) = @_;
  98.   my %vcard;
  99.  
  100.   my @fields = parse_line(',', 0, $line);
  101.  
  102.   my %vcard_def = ( FN => 'Title |First Name |Middle Name |Last Name |Suffix',
  103.             N => 'Last Name| Suffix|;First Name|;Middle Name|;Title',
  104.            'ADR;WORK'  => 'PO Box|;Business Street 2|;Business Street|;Business City|;Business State|;Business Postal Code|;Business Country',
  105.            'LABEL;QUOTED-PRINTABLE;WORK' => 'PO Box |Business Street=0A|Business Street 2=0A|Business City,| Business State| Business Postal Code=0A|Business Country',
  106.            'TEL;WORK;VOICE' => 'Business Phone',
  107.            'TEL;WORK;VOICE2' => 'Business Phone 2',
  108.            'TEL;WORK;FAX' => 'Business Fax',
  109.            'TEL;WORK;COMPANY' => 'Company Main Phone',
  110.            'ADR;HOME'  => ';Home Street 2|;Home Street|;Home City|;Home State|;Home Postal Code|;Home Country',
  111.            'LABEL;QUOTED-PRINTABLE;HOME' => 'Home Street=0A|Home Street 2=0A|Home City,| Home State| Home Postal Code=0A|Home Country',
  112.            'TEL;HOME;VOICE' => 'Home Phone',
  113.            'TEL;HOME;VOICE2' => 'Home Phone 2',
  114.            'TEL;HOME;FAX' => 'Home Fax',
  115.            'ADR;POSTAL'  => ';Other Street 2|;Other Street|;Other City|;Other State|;Other Postal Code|;Other Country',
  116.            'LABEL;QUOTED-PRINTABLE;POSTAL' => 'Other Street=0A|Other Street 2=0A|Other City,| Other State| Other Postal Code=0A|Other Country',
  117.            'TEL;VOICE' => 'Other Phone',
  118.            'TEL;FAX' => 'Other Fax',
  119.            'TEL;CELL' => 'Mobile Phone',
  120.            'TEL;CAR' => 'Car Phone',
  121.            'TEL;PAGER' => 'Pager',
  122.            'TEL;PREF' => 'Primary Phone',
  123.            'TEL;ISDN' => 'ISDN',
  124.            'TEL;X-EVOLUTION-CALLBACK' => 'Callback',
  125.            'TEL;X-EVOLUTION-TTYTDD' => 'TTY/TDD Phone',
  126.            'TEL;X-EVOLUTION-TELEX' => 'Telex',
  127.            'TEL;X-EVOLUTION-RADIO' => 'Radio Phone',
  128.            'EMAIL;INTERNET' => 'E-mail Address',
  129.            'EMAIL;INTERNET2' => 'E-mail 2 Address',
  130.            'EMAIL;INTERNET3' => 'E-mail 3 Address',
  131.             ORG => 'Company|;Department',
  132.             TITLE => 'Job Title',
  133.             ROLE => 'Profession',
  134.                    'X-EVOLUTION-ASSISTANT' => "Assistant's Name",
  135.            'TEL;X-EVOLUTION-ASSISTANT' => "Assistant's Phone",
  136.            'X-EVOLUTION-SPOUSE' => 'Spouse',
  137.                    'X-EVOLUTION-ANNIVERSARY' => 'Anniversary',
  138.                    'X-EVOLUTION-MANAGER' => "Manager's Name",
  139.                    'X-EVOLUTION-OFFICE' => 'Office Location',
  140.             BDAY => 'Birthday',
  141.             NOTE => 'Notes',
  142.             FBURL => 'Internet Free Busy',
  143.             URL => 'Web Page',
  144.                    );
  145.  
  146.   foreach my $key (keys(%vcard_def)) {
  147.     my $attr = build_vcard_attr_from_def($vcard_def{ $key }, \@fields, \%map);
  148.     if (defined($attr)) {
  149.       $vcard{ $key } = $attr unless ($attr =~ /^$/);
  150.     }
  151.   } 
  152.  
  153.   return %vcard;
  154. }
  155.  
  156. sub print_vcard_to_fh
  157. {
  158.   my ($fh, %vcard) = @_;
  159.  
  160.   print $fh "BEGIN:VCARD\n";
  161.   foreach my $key (keys(%vcard)) {
  162.     # Dirty hack because Evolution's vcard stores multiple email addrs
  163.     # with same sttribute, hence key collision.  Bleah.
  164.     # Ugh!  Same deal for multiple phones... (eg. bus. phone)
  165.     #
  166.     # And finally, while we're special-casing...  Outlook exports dates
  167.     # differently, so munge 'em if we find 'em.
  168.     if ($key =~ /EMAIL;INTERNET/o) {
  169.       (my $temp = $key) =~ s/\d$//;
  170.       print $fh "$temp:$vcard{ $key }\n";
  171.     } elsif ($key =~ /TEL;(HOME|WORK)/o) {
  172.       (my $temp = $key) =~ s/\d$//;
  173.       print $fh "$temp:$vcard{ $key }\n";
  174.     } elsif ($key =~ /(BDAY|X\-EVOLUTION\-ANNIVERSARY)/o) {
  175.       my $temp = $vcard{ $key };
  176.       if ($temp =~ /(\d\d)\/(\d\d)\/(\d\d)/) {
  177.         # Y2k !!  MS Didn't learn anything.
  178.         # Hope no one was born before 1915
  179.         if ((1900 + $3) < 1915) {
  180.           print $fh "$key:20$3-$1-$2\n";
  181.         } else {
  182.           print $fh "$key:19$3-$1-$2\n";
  183.         }
  184.       } else {
  185.         # Something's funky...  Just delete the attribute
  186.         print STDERR "Couldn't figure out what to do with $key:$vcard{ $key }\n";
  187.         delete($vcard{ $key });
  188.       } 
  189.     } else {
  190.       print $fh "$key:$vcard{ $key }\n";
  191.     }
  192.   }
  193.   print $fh "END:VCARD\n\n";
  194. }
  195.  
  196. my $in  = $ARGV[0];
  197. my $out = $ARGV[1];
  198.  
  199. usage() unless(defined($in) && defined($out));
  200.  
  201. open (IN, $in)
  202.   or die "Can't open($in): $!\n";
  203.  
  204. open (OUT, ">$out")
  205.   or die "Can't open($out): $!\n";
  206.  
  207. my $linectr = 0;
  208. my %map;
  209.  
  210. while (my $line = <IN>) {
  211.   $line =~ s/\r//g;
  212.   $line =~ s/\n$//;
  213.   if ($linectr == 0) {
  214.     $linectr++;
  215.     usage() unless is_recognized_format($line);
  216.     %map = map_columns($line);
  217.     #if ($line =~ /\r\n$/) {
  218.     #  print STDERR "Apparenlty found DOS-style EOL indicators...\n";
  219.       $/ = "\r\n";
  220.     #}
  221.   } else {
  222.     $linectr++;
  223.     while ($line =~ /^(("([^"]|\n|"")*")?,)*"([^"]|\n|"")*$/) {
  224.       my $temp = $line;
  225.       $line = <IN>;
  226.       $line =~ s/\r//g;
  227.       $line =~ s/\n$//;
  228.       $line = "$temp $line";
  229.     }
  230.     my %vcard = build_vcard_from_line($line, %map);
  231.     print_vcard_to_fh(\*OUT, %vcard);
  232.   }
  233. }  
  234.  
  235. close(IN);
  236. close(OUT);
  237.